#include "multiprocessor/multiprocessor.h"
#include <unistd.h>
#include <ctime>
#include <time.h>

#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/cursorstreamwrapper.h"
#include "rapidjson/writer.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"

namespace CityFlow{
    multiprocessor::multiprocessor(const std::string &configFile)
    {
        std::string cconfigFile = "/home/zhj/Desktop/CityFlow/build/6_6_m/out_config_6_6.json";
        loadFromConfig(cconfigFile);

        // std::cout << "end of initengines" << std::endl;

        // std::vector<std::thread> threads;
        // for (size_t i = 0; i < multiprocessor::engines.size(); ++i)
        // {
        //     threads.emplace_back(std::thread(&multiprocessor::initEngines,this,i));
        // }
        // for (size_t i = 0; i < threads.size(); i++)
        // {
        //     threads[i].join();
        // }
        // for (size_t i = 0; i < multiprocessor::engines.size(); ++i)
        // {
        //     multiprocessor::engines[i]->startThread();
        // }
        // std::cout << "end of init" << std::endl;
    }


    bool multiprocessor::loadFromConfig(std::string jsonFileName)   // load engines from an all-in-one config
    {
        rapidjson::Document document;
        if (!readJsonFromFile(jsonFileName, document)) {
            std::cerr << "cannot open roadnet file" << std::endl;
            return false;
        }
        std::list<std::string> path;
        if (!document.IsObject())
            throw JsonTypeError("roadnet config file", "object");
        try {
            const rapidjson::Value &engineConfigs = getJsonMemberArray("engines", document);

            //  build mapping
            for (rapidjson::SizeType i = 0; i < engineConfigs.Size(); i++){
                engines.emplace_back();
            }
            for (rapidjson::SizeType i = 0; i < engineConfigs.Size(); i++) {
                const auto &curEngineConfig = engineConfigs[i];
                if (!curEngineConfig.IsObject()) {
                    throw JsonTypeError("engineConfig", "object");
                    return false;
                }
                std::string path_t = getJsonMember<const char*>("engineDir", curEngineConfig);
                path_t += getJsonMember<const char*>("configFile", curEngineConfig);
                std::cerr << path_t << std::endl;
                Engine *engine = new Engine(path_t, 1, this, i);// TODO read roadnet faster
                engines[i] = engine;
            }
        }catch (const JsonFormatError &e) {
            std::cerr << "Error occurred when reading the roadnet file: " << std::endl;
            for (const auto &node : path) {
                std::cerr << "/" << node;
            }
            std::cerr << " " << e.what() << std::endl;
            return false;
        }
        return true;
    }

    void multiprocessor::engineNext(int i){
        multiprocessor::engines[i]->nextStep();
    }

    void multiprocessor::calDensity(int engineId, int flowId){
        engines[engineId]->virtualFlows[flowId]->calDensity();
    }

    void multiprocessor::nextStepPro_F(){
        // clock_t start, now;
        // start = clock();
        std::vector<std::thread> threads1;
        for(size_t i = 0; i < multiprocessor::engines.size(); i++)
        {
            threads1.emplace_back(std::thread(&multiprocessor::engineNext,this,i));
        }
        for (size_t i = 0; i < threads1.size(); i++)
        {
            threads1[i].join();
        }
        // now = clock();
        // std::cerr << "50 steps" << now - start << std::endl;

        // start = clock();
        step++;
        if (step % syncRate == 0)
        {
            std::vector<std::thread> threads2;
            for (size_t i = 0; i < multiprocessor::engines.size(); i++)
            {
                for (size_t j = 0; j < (engines[i])->virtualFlows.size(); j++)
                {
                    threads2.emplace_back(std::thread(&multiprocessor::calDensity,this,i,j));
                }
            }
            for (size_t i = 0; i < threads2.size(); i++)
            {
                threads2[i].join();
            }
        }
        // now = clock();
        // std::cerr << "sync" << now - start << std::endl;
    }
}